﻿#pragma once

#include "box_network_event.hh"
#include "klogger/interface/logger.h"
#include "lang/lang.hh"
#include "util/box_std_allocator.hh"
#include "util/spsc_queue.hpp"
#include "util/spsc_queue_pair.hpp"
#include <atomic>
#include <memory>
#include <string>
#include <thread>
#include <unordered_map>

namespace kratos {
namespace config {
class BoxConfig;
}
} // namespace kratos

namespace klogger {
class Appender;
} // namespace klogger

namespace kratos {
namespace loop {
class Loop;
class LoopFactory;
} // namespace loop
} // namespace kratos

namespace kratos {
namespace service {

class BoxChannel;
class ServiceBox;
using BoxChannelPtr = std::shared_ptr<BoxChannel>;
using SPSCQueue = kratos::corelib::SPSCQueue<NetEventData>;
using SPSCQueuePair = kratos::corelib::SPSCQueuePair<NetEventData>;
/**
 * 管道信息
 */
struct ChannelInfo {
  BoxChannelPtr channel_ptr;              ///< 管道指针
  SPSCQueuePair *main_queue_ptr{nullptr}; ///< 管道主线程消息队列
};
using LoopFactoryPtr = std::shared_ptr<loop::LoopFactory>;
using LoopPtr = std::shared_ptr<loop::Loop>;
using MainChannelMap = PoolUnorederedMap<std::uint64_t, ChannelInfo>;
using LoopFactoryMap = PoolUnorederedMap<std::string, LoopFactoryPtr>;
using ListenerNameMap =
    PoolUnorederedMap<std::uint64_t, std::pair<std::uint64_t, std::string>>;

/**
 * Loop信息
 */
struct LoopCore {
  std::thread thread_;                  ///< 网络线程
  std::atomic_bool running_{false};     ///< 网络线程运行标志
  std::atomic_bool thread_exit_{false}; ///< 网络线程退出标志
  SPSCQueue
      main_to_net_queue; ///< 单读单写队列-生产者：主线程，消费者：网络线程
  SPSCQueue
      net_to_main_queue; ///< 单读单写队列-生产者：网络线程，消费者：主线程
  SPSCQueuePair main_queue{&net_to_main_queue,
                           &main_to_net_queue}; ///< 队列对，方便读写
  SPSCQueuePair net_queue{&main_to_net_queue,
                          &net_to_main_queue}; ///< 队列对，方便读写
  LoopPtr loop_;                               ///< 循环
  std::string type_;                           ///< loop type
};
using LoopCorePtr = std::shared_ptr<LoopCore>;
using LoopCoreVector = PoolVector<LoopCorePtr>;
/**
 * 网络
 */
class BoxNetwork {
  LoopCoreVector loop_core_vec_;
  MainChannelMap main_channel_map_;   ///< 主线程管道查找表
  ListenerNameMap listener_name_map_; ///< 监听器名字查找表
  int channel_recv_buffer_len_{1024 *
                               16}; ///< 当前管道读缓冲区长度, 默认为1024*16
  LoopFactoryMap loop_factory_map_; ///< {Loop类型, Loop工厂}
  std::thread::id main_thr_id_;     ///< 主线程ID

public:
  /**
   * 构造
   */
  BoxNetwork();
  /**
   * 析构
   */
  virtual ~BoxNetwork();
  /**
   * @brief 启动网络，内部会启动一个独立的网络线程.
   * @return true或false
   */
  auto start() -> bool;
  /**
   * 关闭，关闭并等待网络线程关闭.
   * @retval true 成功
   * @retval false 失败
   */
  auto stop() -> bool;
  /**
   * 启动一个网络监听器, 监听host:port.
   * @param name 名称
   * @param type network type
   * @param host 网络地址或域名
   * @param port 端口
   * @retval true 成功
   * @retval false 失败
   */
  auto listen_at(const std::string name, const std::string &type,
                 const std::string &host, int port) -> bool;
  /**
   * 启动一个连接器.
   * @param name 名称
   * @param type network type
   * @param host 网络地址或域名
   * @param port 端口
   * @retval true 成功
   * @retval false 失败
   */
  auto connect_to(const std::string name, const std::string &type,
                  const std::string &host, int port, int timeout) -> bool;
  /**
   * 关闭管道.
   * @param id 管道ID
   * @retval true 成功
   * @retval false 失败，失败不论什么原因都会正确释放资源
   */
  auto close_channel(std::uint64_t id) -> bool;
  /**
   * 网络主循环，在逻辑主循环内调用.
   */
  auto update() -> void;
  /**
   * 获取管道实例
   *
   * \param id 管道ID
   * \return 管道实例
   */
  auto get_channel(std::uint64_t id) const noexcept -> const BoxChannelPtr &;
  /**
   * 获取网络线程队列
   *
   * \return 网络线程队列
   */
  auto get_net_queue() noexcept -> SPSCQueuePair &;
  /**
   * 获取逻辑线程队列
   *
   * \return 逻辑线程队列
   */
  auto get_main_queue() noexcept -> SPSCQueuePair &;
  /**
   * 获取监听器名字表
   */
  auto get_listener_name_map() noexcept -> ListenerNameMap &;
  /**
   * 获取管道接收缓冲区长度
   *
   * \return 接收缓冲区长度
   */
  auto get_channel_recv_buffer_len() const noexcept -> int;
  /**
   * 注册Loop工厂
   *
   * \param type Loop类型
   * \param loop_factory_ptr Loop工厂
   *
   * \return true成功, false失败
   */
  virtual auto register_loop_factory(const std::string &type,
                                     LoopFactoryPtr loop_factory_ptr) -> bool;
  /**
   * 在worker线程内发送listen_request
   */
  virtual auto listen_response(const std::string &name,
                               std::uint64_t channel_id, bool success) -> bool;
  /**
   * 在worker线程内发送accept_notify
   */
  virtual auto accept_notify(const std::string &name, std::uint64_t channel_id,
                             const std::string &local_ip, int local_port,
                             const std::string &peer_ip, int peer_port) -> bool;
  /**
   * 在worker线程内发送connect_response
   */
  virtual auto connect_response(const std::string &name,
                                std::uint64_t channel_id,
                                const std::string &local_ip, int local_port,
                                const std::string &peer_ip, int peer_port,
                                bool success) -> bool;
  /**
   * 在worker线程内发送recv_data_notify
   */
  virtual auto recv_data_notify(std::uint64_t channel_id, char *data,
                                int length) -> bool;
  /**
   * 在worker线程内发送close_notify
   */
  virtual auto close_notify(std::uint64_t channel_id) -> bool;
  /**
   * 建立新的UUID
   */
  virtual auto gen_uuid() -> std::uint64_t;

public:
  /**
   * 获取服务容器配置
   *
   * \return 服务容器配置
   */
  virtual auto get_config() -> kratos::config::BoxConfig & = 0;
  /**
   * 获取日志添加器.
   *
   * \return
   */
  virtual auto get_logger_appender() -> klogger::Appender * = 0;

  /**
   * 获取本地化实例.
   *
   * \return 本地化实例
   */
  virtual auto get_lang() -> lang::Lang * = 0;
  /**
   * 开启监听事件
   *
   * \param name 监听器名称
   * \param success 成功或失败
   * \param channel 监听管道
   */
  virtual void on_listen(const std::string &name, bool success,
                         BoxChannelPtr &channel) = 0;
  /**
   * 接受一个新的连接建立
   *
   * \param channel 新建立的管道
   */
  virtual void on_accept(BoxChannelPtr &channel) = 0;
  /**
   * 连接到一个远程监听器
   *
   * \param name 连接器名称
   * \param success 成功或失败
   * \param channel 新建立的管道
   */
  virtual void on_connect(const std::string &name, bool success,
                          BoxChannelPtr &channel) = 0;
  /**
   * 关闭管道事件
   *
   * \param channel 关闭的管道
   */
  virtual void on_close(BoxChannelPtr &channel) = 0;
  /**
   * 数据事件
   *
   * \param channel 接收到数据的管道
   */
  virtual void on_data(BoxChannelPtr &channel) = 0;

private:
  /**
   * 检测是否在主线程内
   */
  auto is_main_thread() -> bool;
  /**
   * @brief 启动网络，内部会启动一个独立的网络线程.
   * @param type 网络类型
   * @return LoopCorePtr
   */
  auto start_internal(const std::string &type) -> LoopCorePtr;
  /**
   * 启动网络，内部会启动一个独立的网络线程.
   *
   * @param loop_core_ptr 循环核心
   * @retval true 成功
   * @retval false 失败
   */
  auto start(LoopCorePtr loop_core_ptr) -> bool;
  /**
   * @brief 启动网络循环
   * @param loop_type 循环类型
   * @return LoopPtr
   */
  auto create_and_start_loop(const std::string &loop_type) -> LoopPtr;
  /**
   * 网络线程事件处理函数.
   *
   * @param loop_core_ptr 循环核心
   */
  auto network_thread_processor(LoopCorePtr loop_core_ptr) -> void;
  /**
   * 向网络线程发送数据
   *
   * \param id 管道ID
   * \param data 数据指针
   * \param size 数据长度
   * \return 实际发送的数据长度
   */
  auto enqueue_send_request(std::uint64_t id, const char *data, int size)
      -> int;
  /**
   * 通知网络线程关闭连接
   *
   * \param id 管道ID
   * \return 成功或失败
   */
  auto enqueue_close_request(std::uint64_t id) -> bool;
  /**
   * 启动网络线程
   *
   * @param loop_core_ptr 循环核心
   */
  auto start_worker(LoopCorePtr loop_core_ptr) -> void;
  /**
   * 网络线程清理
   *
   * @param loop_core_ptr 循环核心
   */
  auto worker_cleanup(LoopCorePtr loop_core_ptr) -> void;
  /**
   * 在主线程内处理网络事件
   */
  auto do_event_main(LoopCorePtr loop_core_ptr, NetEventData &event_data)
      -> void;
  /**
   * 配置变化重载.
   *
   * \return
   */
  auto on_config_change() -> void;

  /**
   * 写入日志.
   *
   * \param id 语言ID
   * \param level 日志等级
   * \param args 日志参数
   * \return
   */
  template <typename... ARGS>
  auto write_log(lang::LangID id, int level, ARGS... args) {
    if (!get_lang() || !get_logger_appender()) {
      return;
    }
    const auto &fmt = get_lang()->get_lang(id);
    if (fmt.empty()) {
      return;
    }
    get_logger_appender()->write(level, fmt.c_str(), args...);
  }
  /**
   * 获取Loop.
   *
   * \param type Loop类型
   * \return Loop实例
   */
  auto create_loop(const std::string &type) -> LoopPtr;

  friend class BoxChannel;
};

} // namespace service
} // namespace kratos
